package com.gmlive.common.okhttpquality;

import android.text.TextUtils;

import com.gmlive.common.okhttpquality.internal.IkOkHttpMonitor;
import com.google.gson.Gson;

import java.io.EOFException;
import java.io.IOException;
import java.net.URL;
import java.nio.charset.Charset;
import java.nio.charset.UnsupportedCharsetException;
import java.util.concurrent.TimeUnit;

import okhttp3.Headers;
import okhttp3.HttpUrl;
import okhttp3.Interceptor;
import okhttp3.MediaType;
import okhttp3.Request;
import okhttp3.Response;
import okhttp3.ResponseBody;
import okhttp3.internal.http.HttpHeaders;
import okio.Buffer;
import okio.BufferedSource;

/**
 * 记录网络请求成功或失败的拦截器
 *
 * @作者: 姜志敏
 * @日期: 2019-08-17
 */
public class IkOkHttpQualityInterceptor implements Interceptor {
    private static final Charset UTF8 = Charset.forName("UTF-8");

    private Gson gson = new Gson();

    private final QualityCallback callback;

    public interface QualityCallback {
        void onAccept(IkOkHttpMonitor monitor);
    }


    public IkOkHttpQualityInterceptor(QualityCallback callback) {
        this.callback = callback;
    }

    @Override
    public Response intercept(Chain chain) throws IOException {
        IkOkHttpMonitor monitor = new IkOkHttpMonitor();
        long startNs = System.nanoTime();
        long tookMs = 0;
        Request request = chain.request();
        Response response;
        try {
            response = chain.proceed(request);
            tookMs = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startNs);
            if (response != null) {
                checkResponse(request, response, monitor);
            }
            return response;
        } catch (Exception e) {
            setHttpError(request, monitor, e, IkOkHttpMonitor.Type.HTTP);
            throw e;
        } finally {
            if (callback != null && !TextUtils.isEmpty(monitor.getUrl())) {
                monitor.setTookMs(tookMs);
                callback.onAccept(monitor);
            }
        }
    }

    private void checkResponse(Request request, Response response, IkOkHttpMonitor monitor){
        HttpUrl httpUrl = request.url();
        URL url = httpUrl.url();
        if(url.toString().contains("log/upload")){//过滤 log api
            return;
        }
        proceedResponseForHttp(request, response, monitor);//检测错误
        if(TextUtils.isEmpty(monitor.getUrl())){ //没有发现错误
            setUrlInfo(request, monitor);
            monitor.setSuccessful(true);
            monitor.setCode(response.code());
        }
    }

    private void proceedResponseForHttp(Request request, Response response, IkOkHttpMonitor monitor) {
        int code = response.code();
        if(response.isSuccessful()){
            //http 成功，检查业务
            try {
                proceedResponseForBusiness(request, response, monitor);
            } catch (Exception e) {
                setHttpError(request, monitor, e, IkOkHttpMonitor.Type.BUSINESS);
            }
        }else{
            setUrlInfo(request, monitor);
            monitor.setType(IkOkHttpMonitor.Type.HTTP);
            monitor.setCode(code);
            monitor.setMessage(response.message());
        }
    }

    // 解析网络请求，业务层错误
    private void proceedResponseForBusiness(Request request, Response response, IkOkHttpMonitor monitor) throws Exception {
        if (!HttpHeaders.hasBody(response)) {
            return;
        }
        if (bodyEncoded(response.headers())) {
            return;
        }

        ResponseBody responseBody = response.body();
        Charset charset = UTF8;
        MediaType contentType = responseBody.contentType();
        if (contentType != null) {
            try {
                charset = contentType.charset(UTF8);
            } catch (UnsupportedCharsetException e) {
                return;
            }
        }

        long contentLength = responseBody.contentLength();
        BufferedSource source = responseBody.source();
        source.request(Long.MAX_VALUE); // Buffer the entire body.
        Buffer buffer = source.buffer();
        if (!isPlaintext(buffer)) {
            return;
        }
        if (contentLength == 0) {
            return;
        }
        String result = buffer.clone().readString(charset);
        IkOkHttpQualityReporter.IkApi ikApi = gson.fromJson(result, IkOkHttpQualityReporter.IkApi.class);
        int code = ikApi.getCode();
        if(code == 0){ //接口返回成功
            return;
        }

        setUrlInfo(request, monitor);
        monitor.setType(IkOkHttpMonitor.Type.BUSINESS);
        monitor.setCode(code);
        monitor.setMessage(ikApi.getMessage());
    }

    // 解析网络请求，http层错误
    private void setHttpError(Request request, IkOkHttpMonitor monitor, Exception e, IkOkHttpMonitor.Type type) {
        setUrlInfo(request, monitor);
        monitor.setType(type);
        monitor.setCode(-1);
        monitor.setMessage(e.getMessage());
    }

    // 获取请求信息
    private void setUrlInfo(Request request, IkOkHttpMonitor monitor) {
        HttpUrl httpUrl = request.url();
        URL url = httpUrl.url();
        monitor.setUrl(url.toString());
        monitor.setApi(url.getPath());
    }

    private static boolean isPlaintext(Buffer buffer) {
        try {
            Buffer prefix = new Buffer();
            long byteCount = buffer.size() < 64 ? buffer.size() : 64;
            buffer.copyTo(prefix, 0, byteCount);
            for (int i = 0; i < 16; i++) {
                if (prefix.exhausted()) {
                    break;
                }
                int codePoint = prefix.readUtf8CodePoint();
                if (Character.isISOControl(codePoint) && !Character.isWhitespace(codePoint)) {
                    return false;
                }
            }
            return true;
        } catch (EOFException e) {
            return false; // Truncated UTF-8 sequence.
        }
    }

    private boolean bodyEncoded(Headers headers) {
        String contentEncoding = headers.get("Content-Encoding");
        return contentEncoding != null && !contentEncoding.equalsIgnoreCase("identity");
    }

}
